function [Dh, Dv, Dd1, Dd2] = extract_details_filters(varargin)
%EXTRACT_DETAILS_FILTERS Extracts horizontal, vertical and diagonal details on
% multiple scales using custom filters. 
%
% Syntax:
%   [Dh, Dv, Dd1, Dd2] = extract_details_filters(I, params)
%   [Dh, Dv, Dd1, Dd2] = extract_details_filters(I)
% Inputs:
%   I - Input gray scale image.
%   params - Structure with parameter valued.
%       .num_iterations - Number of filtering iteration
%                         Possible values: 1 - 4
%                         4 (default)
%
%       .enable_pre_smoothing - Enables image smoothing before entering
%                               filtering iteration process. Gaussian
%                               function is used.
%                               Possible values: true (default), false
%
%       .enable_pre_averaging - Enables additional smoothing by applying
%                               additional averaging filter on smoothed
%                               result. Takes effect only if
%                               enable_pre_smoothing is true.
%                               Possible values: true (default), false
%
%       .enable_smoothing - Enables smoothing at each iteration before
%                           detail extraction
%                           Possible values: true (default), false
%
%       .enable_averaging - Enables additional smoothing by applying
%                           additional averaging filter on smoothed result.
%                           Takes effect only if enable_smoothing is true.
%                           Possible values: true (default), false
%
%       .scale_before_smoothing - Scales image by factor of 2 before
%                                 smoothing operations. Takes effect only if
%                                 enable_smoothing is true.
%                                 Possible values: true (default), false
%
%       .upscale_before_smoothing - If scaling before smoothing is enabled
%                                   then if this variable is true image is
%                                   first upscaled then filtered and
%                                   downscaled by factor of 2. If this
%                                   variable is false and scaling is
%                                   enables then image is first downscaled
%                                   then filtered and upscaled by factor of
%                                   2.
%                                   Possible values: true (default), false
%
%       .enable_filter_interpolation - If this variable is true then
%                                      starting filters (for first
%                                      iteration) are upsampled in each
%                                      iteration by the factor of 2.                                      
%                                      Possible values: true (default), false
%
%       .smoothing_filter_size - Array of smoothing gaussian filter sizes for each
%                                iteration of processing. Used only if
%                                enable_pre_smoothing or enable_smoothing
%                                are set.
%                                
%                                Default: [3, 7, 11, 9]
%
%       .smoothing_filter_sigma - Array of smoothing gaussian filter sigmas
%                                 for each iteration of processing. Used only if
%                                 enable_pre_smoothing or enable_smoothing
%                                 are set.
%                                
%                                 Default: [1, 1.5, 1.5, 1]
%
%       .average_filter_size - Array of average filter sizes for each
%                              iteration of processing. Used only if
%                              enable_smoothing and enable_averaging or
%                              enable_pre_smoothing and
%                              enable_pre_averaging are set.
%                                
%                              Default: [3, 3, 3, 5]
%
%       .horizontal_filter - 2D horizontal details filter
%                           [ 1  2  1
%                             0  0  0
%                            -1 -2 -1]/4 - default
%       .vertical_filter - 2D vertical details filter
%                          [1 0 -1
%                           2 0 -2
%                           1 0 -1]/4 - default
%       .d1_filter - 2D diagonal +45 degrees details filter
%                          [ 0  0  1  0  0
%                            0  2  0  0  0
%                            1  0  0  0 -1
%                            0  0  0 -2  0
%                            0  0 -1  0  0]/4 - default                            
%
%       .d2_filter - 2D diagonal -45 degrees details filter
%                          [ 0  0  1  0  0
%                            0  0  0  2  0
%                           -1  0  0  0  1
%                            0 -2  0  0  0
%                            0  0 -1  0  0]/4 - default  
%
% Outputs:
%   Dh - Array of horizontal detail planes (num_iterations planes).
%   Dv - Array of vertical detail planes (num_iterations planes).
%   Dd1 - Array of diagonal +45 degrees detail planes (num_iterations planes).
%   Dd2 - Array of diagonal -45 degrees detail planes (num_iterations planes).
%
% Example:
%
% Created on:    2014-01-13 (Dragomir El Mezeni)
% Last revision: 2014-01-13 (Dragomir El Mezeni)

%------------- BEGIN CODE --------------

    params = struct('num_iterations', 4, ...
                    'enable_pre_smoothing', true, ...
                    'enable_pre_averaging', true, ...
                    'enable_smoothing', true, ...
                    'enable_averaging', true, ...
                    'scale_before_smoothing', true, ...
                    'upscale_before_smoothing', true, ...
                    'enable_filter_interpolation', false, ...
                    'smoothing_filter_size', [3, 7, 11, 9], ...
                    'smoothing_filter_sigma', [1, 1.5, 1.5, 1], ...
                    'average_filter_size', [3, 3, 3, 5], ...
                    'horizontal_filter', [1, 2, 1; 0, 0, 0; -1, -2, -1]./4, ...
                    'vertical_filter', [1, 0, -1; 2, 0, -2; 1, 0, -1]./4, ...
                    'd1_filter', [1, 2, 0; 2, 0, -2; 0, -2, -1]./4, ...
                    'd2_filter', [0, 2, 1; -2, 0, 2; -1, -2, 0]./4);
                        
    switch nargin
        case 1
            % Nothing to be done
        case 2
            params = merge_structs(params, varargin{2});
        otherwise
        error('Invalid number of input args.');
    end
    
    I = varargin{1};
    
    sf = fspecial('gaussian', params.smoothing_filter_size(1), params.smoothing_filter_sigma(1));
    af = fspecial('average', params.average_filter_size(1));
    hf = params.horizontal_filter;
    vf = params.vertical_filter;
    d1f = params.d1_filter;
    d2f = params.d2_filter;
 
    %% Image pre smoothing
    if (params.enable_pre_smoothing)
        I = conv2(I,  sf, 'same');
        if (params.enable_pre_averaging)
            I = conv2(I, af, 'same');
        end
    end
    
    for iter = 1:1:params.num_iterations    
        %% Image smoothing
        if (params.enable_smoothing && (iter > 1))
            if (params.scale_before_smoothing)
                if (params.upscale_before_smoothing)
                    Is = imresize(I, 2);
                else
                    Is = imresize(I, 0.5);
                end
            else
                Is = I;
            end
            
            Is = conv2(Is,  sf, 'same');
            if (params.enable_averaging)
                Is = conv2(Is, af, 'same');
            end
            
            if (params.scale_before_smoothing)
                if (params.upscale_before_smoothing)
                    Is = imresize(Is, 0.5);
                else
                    Is = imresize(Is, 2);
                end
            end
        else
            Is = I;
        end
        
        %% Detail extractions
        Dh{iter} = conv2(Is, hf, 'same');
        Dv{iter} = conv2(Is, vf, 'same');
        Dd1{iter} = conv2(Is, d1f, 'same');
        Dd2{iter} = conv2(Is, d2f, 'same');
        
        if (params.enable_filter_interpolation)
            %% Filter upscaling
            sf = imresize(sf, ((size(sf) - [1,1]).*2 + [1,1]));
            af = imresize(af, ((size(af) - [1,1]).*2 + [1,1]));
            hf = imresize(hf, ((size(hf) - [1,1]).*2 + [1,1]));        
            vf = imresize(vf, ((size(vf) - [1,1]).*2 + [1,1]));        
            d1f = imresize(d1f, ((size(d1f) - [1,1]).*2 + [1,1]));        
            d2f = imresize(d2f, ((size(d2f) - [1,1]).*2 + [1,1]));

            %% Filter normalization
            sf = sf./sum(sf(:));
            hf = hf./sum(hf(hf>0));
            vf = vf./sum(vf(vf>0));
            d1f = d1f./sum(d1f(d1f>0));
            d2f = d2f./sum(d2f(d2f>0));
        else
            if (iter < params.num_iterations)
                sf = fspecial('gaussian', params.smoothing_filter_size(iter + 1), ...
                               params.smoothing_filter_sigma(iter + 1));
                           
                af = fspecial('average', params.average_filter_size(iter + 1));
            end
        end
    end 
    
end
